#include <iostream>

#include "hiredis/hiredis.h"

#include "cpp-hiredis-cluster/include/cluster.h"
#include "cpp-hiredis-cluster/include/hirediscommand.h"

#include "crow/amalgamate/crow_all.h"

using namespace std;

// Declare cluster pointer
static RedisCluster::Cluster<redisContext>::ptr_t cluster_p = RedisCluster::HiredisCommand<>::createCluster( "192.168.1.201", 7401);
// Declare pointer to simple hiredis reply structure
// Create cluster passing acceptable address and port of one node of the cluster nodes

int main() {

    crow::SimpleApp app;

    crow::logger::setLogLevel(crow::LogLevel::CRITICAL);

//    char* listad = "listAdsOfBeaconsMem.action";

    CROW_ROUTE(app, "/")([](){
        return "Hello world";
    });


    CROW_ROUTE(app, "/testConn.action") ([]{
        return "OK";
    });

    CROW_ROUTE(app, "/listAdsOfBeaconsMem.action") ([](const crow::request& req){
//        std::ostringstream os;

        redisReply * reply;

        char buf[10240];
        char buf2[1024];
        char buf3[1024];
        char buf4[10240];

        char* endUserSn = req.url_params.get("endUserSn");
        char* triggerType = req.url_params.get("triggerType");
        char* appToken = req.url_params.get("appToken");
        char* jsonBeacons = req.url_params.get("jsonBeacons");

        if (jsonBeacons == nullptr) {
            return crow::response{400};
        }

        int i = 0;
        while (jsonBeacons[i++]) {
            jsonBeacons[i] = (char) toupper(jsonBeacons[i]);
        }

        auto beacons = crow::json::load(jsonBeacons);

        if (!beacons) {
            return crow::response(400);
        }

        size_t luuid;
        size_t lmajor;
        size_t lminor;

        /*
         * Sort JsonBeacon, store index
         */
        int indexList[beacons.size()];

        for (i = 0; i < beacons.size(); i++) {
            indexList[i] = i;
        }

        int a, b, swap;
        for (b = 0; b < beacons.size() - 1; b++) {
            for (a = 0; a < beacons.size() - 1 - b; a++) {
                if(beacons[indexList[a]]["RSSI"].i() > beacons[indexList[a + 1]]["RSSI"].i()) {
                    swap = indexList[a];
                    indexList[a] = indexList[a + 1];
                    indexList[a + 1] = swap;
                }
            }
        }

        char* tmp;
        char* ret = buf2;
        char* ret2 = buf3;
        char* resp = buf4;

        /*
         *  UMM -> PosId (in buf2)
         */
        for (i = 0; i < beacons.size(); i++) {
            //get crow::rvalue
            auto uuid = beacons[indexList[i]]["PROXIMITYUUID"];
            auto major = beacons[indexList[i]]["MAJOR"];
            auto minor = beacons[indexList[i]]["MINOR"];

            //get length of memory block
            luuid = uuid.size();
            //`major` is `r_value` object, major.end_ - major.start_ -> length
            lmajor = (size_t) (*((char*)&major + sizeof(char*)) - *(char*) &major);
            lminor = (size_t) (*((char*)&minor + sizeof(char*)) - *(char*) &minor);

            tmp = buf;
            strcpy(tmp, "UMM:POSID:");
            tmp += 10;

            strncpy(tmp, uuid.s().begin(), luuid);
            tmp += luuid;
            *(tmp++) = ':';
            memcpy(tmp, *(char**) &major, lmajor);
            tmp += lmajor;
            *(tmp++) = ':';
            memcpy(tmp, *(char**) &minor, lminor);
            tmp += lminor;
            tmp[0] = '\0';

            reply = static_cast<redisReply*>( RedisCluster::HiredisCommand<>::Command( cluster_p, buf, "GET %s", buf ));
            if (reply -> type == REDIS_REPLY_STRING) {
//                cout << "String reply: " << reply -> str << endl;
                strcpy(ret, reply -> str);
                ret += strlen(reply -> str);
                *ret++ = '\0';
            }
            if (ret - buf2 > 1000) {
                break;
            }
        }
        *ret++ = 0;
        *ret++ = 0;

        tmp = buf;
        ret = buf2;
        ret2 = buf3;

        /*
         * PosId -> PlanId (in buf)
         */
        while (ret[0]) {
            tmp = buf;
            strcpy(tmp, "POSID:PLANID:");
            tmp += 13;
            strcpy(tmp, ret);
            tmp += strlen(ret);
            *tmp = '\0';

            reply = static_cast<redisReply*>(
                    RedisCluster::HiredisCommand<>::Command( cluster_p, buf, "ZRANGE %s %d %d %s", buf, 0, -1, "WITHSCORES" ));

            if (reply -> type == REDIS_REPLY_ARRAY) {
//                cout << "Array reply: " << reply -> elements << endl;
                strcpy(ret2, reply -> element[0] -> str);
                ret2 += strlen(reply -> element[0] -> str);
                *ret2++ = '\0';
            } else {
//                cout << "Got Reply: " << reply -> type << endl;
            }
            ret += strlen(ret);
            ret++;
        }
        *ret2++ = '\0';
        *ret2++ = '\0';

        tmp = buf;
        ret = buf2;
        ret2 = buf3;

        /*
         *  PlanId -> Ad
         */
        *resp++ = '[';
        while(ret2[0]) {
            tmp = buf;
            strcpy(tmp, "ADPLAN:ADC:");
            tmp += 11;
            strcpy(tmp, ret2);
            tmp += strlen(ret2);
            *tmp = '\0';

            reply = static_cast<redisReply*>(
                    RedisCluster::HiredisCommand<>::Command( cluster_p, buf, "GET %s", buf ));

            if (reply -> type == REDIS_REPLY_STRING) {
//                cout << "String reply: " << reply -> str << endl;
                strcpy(resp, reply -> str);
                resp += strlen(reply -> str);
                *resp++ = ',';
            }
            ret2 += strlen(ret2);
            ret2++;
        }
        if (*(resp - 1) != '[') {
            resp--;
            *resp++ = ']';
            *resp = '\0';
        } else {
            *resp++ = '\0';
        }

        freeReplyObject( reply );
//        cout << "Resp: " << buf4 << endl;

        return crow::response(buf4);
    });

    app.port(18080).multithreaded().run();

//    freeReplyObject( reply );

    // delete cluster by its pointer
//    delete cluster_p;
    return 0;
}

